package com.katze.boot.system.controller;

import com.katze.boot.common.domain.ActiveUser;
import com.katze.boot.common.domain.LimitType;
import com.katze.boot.common.exception.BusinessException;
import com.katze.boot.common.utils.EncryptUtils;
import com.katze.boot.plugins.redis.interfaces.Limit;
import com.katze.boot.plugins.redis.service.CacheService;
import com.katze.boot.plugins.shiro.annotations.RestControllerMapping;
import com.katze.boot.plugins.shiro.annotations.RestRequestMapping;
import com.katze.boot.plugins.shiro.authentication.JWTToken;
import com.katze.boot.plugins.shiro.authentication.JWTUtils;
import com.katze.boot.system.dao.LoginLogMapper;
import com.katze.boot.system.domain.LoginLog;
import com.katze.boot.system.domain.User;
import com.katze.boot.system.domain.UserSetup;
import com.katze.boot.system.service.CaptchaService;
import com.katze.boot.system.service.LoginLogService;
import com.katze.boot.system.service.UserService;
import com.katze.boot.system.service.UserSetupService;
import com.katze.common.Message;
import com.katze.common.Pagination;
import com.katze.common.controller.BaseController;
import com.katze.common.http.HttpUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.stream.Collectors;


@RestControllerMapping
public class LoginController extends BaseController {

    @Resource
    private UserService userService;
    @Resource
    private CacheService cacheService;
    @Resource
    private UserSetupService userSetupService;
    @Resource
    private LoginLogService loginLogService;
    @Resource
    private LoginLogMapper loginLogMapper;
    @Resource
    private CaptchaService captchaService;

    @Limit(prefix = "limit_login", period = 60, count = 5, limitType = LimitType.IP)
    @RestRequestMapping(value = "/login", method = RequestMethod.POST)
    public Message login(String username, String password, String uuid, String captcha, HttpServletRequest request) throws Exception {
        if (!captchaService.validate(uuid, captcha)) {
            return Message.error("验证码不正确");
        }

        User user = this.userService.findDetailByName(username, EncryptUtils.MD5Encrypt(username, password));

        if (user == null || password == null){
            throw new BusinessException("用户名或密码错误");
        }

        if (User.STATUS_LOCK.equals(user.getStatus())){
            throw new BusinessException("账号已被锁定,请联系管理员！");
        }

        // 更新用户登录信息
        HttpUtils.searchRegion(request, (ip, region) -> {
            user.setLastLoginTime(new Date());
            user.setLastLoginIp(ip);
            user.setLastLoginLocation(region);
        });
        this.userService.updateLoginTime(user);
        this.cacheService.saveUser(user);

        // 保存登录记录
        LoginLog loginLog = new LoginLog();
        loginLog.setUsername(username);
        loginLog.setIp(user.getLastLoginIp());
        loginLog.setLocation(user.getLastLoginLocation());
        this.loginLogService.saveLoginLog(loginLog);

        //todo 优化令牌超时问题
        JWTToken jwtToken = new JWTToken(JWTUtils.sign(user), false);
        this.cacheService.saveActiveUser(user.getUsername(), Double.parseDouble(jwtToken.getExipreAt()));
        this.cacheService.saveToken(user.getUsername(), jwtToken.getToken() + "@" + user.getLastLoginIp(), JWTUtils.EXPIRE_TIME);

        Map<String, Object> userInfo = this.generateUserInfo(jwtToken, user);
        return Message.ok().data(userInfo);
    }

    @RestRequestMapping(value = "/captcha.jpg", desc = "生成验证码")
    public void captcha(HttpServletResponse response, String uuid) {
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");

        //获取图片验证码
        BufferedImage image = captchaService.getCaptcha(uuid);
        try (ServletOutputStream out = response.getOutputStream()) {
            ImageIO.write(image, "jpg", out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RestRequestMapping(value = "/login/log")
    public Message loginLog(Pagination request, LoginLog loginLog) {
        /*Map<String, Object> data = new HashMap<>();
        // 获取系统访问记录
        Long totalVisitCount = loginLogMapper.findTotalVisitCount();
        data.put("totalVisitCount", totalVisitCount);
        Long todayVisitCount = loginLogMapper.findTodayVisitCount();
        data.put("todayVisitCount", todayVisitCount);
        Long todayIp = loginLogMapper.findTodayIp();
        data.put("todayIp", todayIp);
        // 获取近期系统访问记录
        List<Map<String, Object>> lastSevenVisitCount = loginLogMapper.findLastSevenDaysVisitCount(null);
        data.put("lastSevenVisitCount", lastSevenVisitCount);
        User param = new User();
        param.setUsername(username);
        List<Map<String, Object>> lastSevenUserVisitCount = loginLogMapper.findLastSevenDaysVisitCount(param);
        data.put("lastSevenUserVisitCount", lastSevenUserVisitCount);
        return Message.ok().data(data);*/
        return toDataTable(loginLogMapper.findByUsername(loginLog.getUsername()));
    }

    @RestRequestMapping(value = "/online", permits = "user:online")
    public Message userOnline(String username) {
        Set<String> online = this.cacheService.getAllActiveUser();
        List<ActiveUser> activeUsers = this.userService.findUserByUsernames(online);
        if (StringUtils.isNotBlank(username)){
            activeUsers = activeUsers.stream().filter(o -> o.getUsername().contains(username)).collect(Collectors.toList());
        }
        return toDataTable(activeUsers);
    }

    @RestRequestMapping(value = "/kickout/{username}", method = RequestMethod.DELETE, permits = "user:kickout")
    public Message kickout(@PathVariable String username) {
        long rs = this.cacheService.deleteActiveUser(username); // 删除 zset中的记录
        if (rs > 0) rs = this.cacheService.deleteToken(username) != null? 1:0;  // 删除对应的 token缓存
        return rs > 0? Message.ok("用户已强制下线"): Message.error("用户下线失败:未找到相关用户信息，请刷新后再试");
    }

    @RestRequestMapping(value = "/logout/{username}")
    public Message logout(@PathVariable String username) {
        this.kickout(username);
        return Message.ok("退出系统成功");
    }

    @RestRequestMapping(value = "/regist", method = RequestMethod.POST)
    public Message regist(String username, String password) {
        this.userService.regist(username, password);
        return Message.ok("注册成功");
    }

    /**
     * 生成前端需要的用户信息，包括：
     * 1. token
     * 2. Vue Router
     * 3. 用户角色
     * 4. 用户权限
     * 5. 前端系统个性化配置信息
     *
     * @param token token
     * @param user  用户信息
     * @return UserInfo
     */
    private Map<String, Object> generateUserInfo(JWTToken token, User user) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("token", token.getToken());
        userInfo.put("expireTime", token.getExipreAt());

        UserSetup userSetup = this.userSetupService.findByUsername(user.getUsername());
        userInfo.put("userSetup", userSetup);

        user.setPassword("it's a secret");
        user.setId(user.getUsername());
        userInfo.put("user", user);
        return userInfo;
    }
}